package server.controller;

import server.comm.ConnectionHandler;
import java.util.ArrayList;
import java.util.List;
import forum.Forum;
import forum.Member;
import forum.Post;
import forum.Thread;
import messaage.*;

public class GuiAdapter {
	private Controller controller;
	private ArrayList<ConnectionHandler> loggedIns;

	public GuiAdapter() {
		this.controller = new Controller();
		this.loggedIns = new ArrayList<ConnectionHandler>();
	}

	/**
	 * This method adds a new member to the forum.
	 * 
	 * @param regMsg
	 *            : the message which contains the data about the new member.
	 * @return an appropriate message according to results of registration.
	 */
	public Message getRegisterMsg(RegisterMsg regMsg) {
		int res = this.controller.registerUser(regMsg.getUsername(),
				regMsg.getFirstName(), regMsg.getLastName(),
				regMsg.getPassword(), regMsg.getEmail(), regMsg.getGender());
		if (res == 0) {
			// return new OkMsg(regMsg.getUsername(),
			// "user was registered successfully");
			return new RegisteredMsg();
		}
		if (res == -1) {
			return new ErrorMsg("user was not registered successfully");
		}
		return new ErrorMsg("user with such username exists");
	}

	/**
	 * This method logs user in.
	 * 
	 * @param loginMsg
	 *            the message which contains the username and his password to
	 *            login.
	 * @return an appropriate message according to the results of logging in.
	 */
	public Message getLoginMsg(LoginMsg loginMsg) {
		int res = this.controller.login(loginMsg.getUsername(),
				loginMsg.getPassword());
		if (res == 0) {
			return new LoggedInMsg(this.controller.isUserModerator(loginMsg.getUsername()));
		}
		if (res == -1) {
			return new ErrorMsg("passwords are not the same");
		}
		if (res == -2) {
			return new ErrorMsg("User is already logged in");
		}
		return new ErrorMsg("user is not registered");
	}

	/**
	 * This method logs user out.
	 * 
	 * @param logoffMsg
	 *            : The message which contains a username of the user to log
	 *            out.
	 * @return an appropriate message according to the results of logging out.
	 */
	public Message getLogoffMsg(LogoutMsg logoffMsg) {
		int res = this.controller.logoff(logoffMsg.getUsername());
		if (res == 0) {
			return new LoggedOffMsg();
		}
		if (res == -1) {
			return new ErrorMsg("user was not logged off successfully");
		}
		return new ErrorMsg("user is not registered");
	}

	/**
	 * This method removes the specified user from the forum.
	 * 
	 * @param remUsrMsg
	 *            contains the names of the user to remove and user who removes.
	 * @return appropriate message according to the results of the removing.
	 */
	public Message getRemoveUser(RemoveUserMsg remUsrMsg) {
		int res = this.controller.removeUser(remUsrMsg.getUserToRemove());
		if (res == 0)
			return new RemovedUserMsg(remUsrMsg.getUserToRemove());
		if (res == -1)
			return new ErrorMsg("removing user "
					+ remUsrMsg.getUserToRemove()
					+ " failed because this user is not registered");
		if (res == -2)
			return new ErrorMsg("removing user "
					+ remUsrMsg.getUserToRemove()
					+ " failed because the user who removes is not logged in");
		if (res == -3)
			return new ErrorMsg("removing user "
					+ remUsrMsg.getUserToRemove()
					+ " failed because the user who removes is not a moderator");
		// if (res == -4)
		return new ErrorMsg("removing user "
				+ remUsrMsg.getUserToRemove()
				+ " failed because the user who removes is not registered");
	}

	/**
	 * this method adds a selected friend to the list of user's friend.
	 * 
	 * @param friendMsg
	 *            contains the usernames of both.
	 * @return an appropriate message according to the results.
	 */
	public Message getAddUserFriendMsg(AddFriendMsg friendMsg) {
		int res = this.controller.addUserFriend(friendMsg.getUsername(),
				friendMsg.getNameOfFriend());
		if (res == 0)
			// return new OkMsg(friendMsg.getUsername(), "friend "
			// + friendMsg.getNameOfFriend() + "was added successfully");
			return new AddedFriendMsg(friendMsg.getNameOfFriend());
		if (res == -1)
			return new ErrorMsg("Error adding the friend  "
					+ friendMsg.getNameOfFriend());
		return new ErrorMsg("Error occoured");
	}

	/**
	 * This method removes a selected friend from the user's friends' list.
	 * 
	 * @param friendMsg
	 *            contains the usernames of both.
	 * @return an appropriate message according to the results.
	 */
	public Message getRemoveUserFriendMsg(RemoveFriendMsg friendMsg) {
		int res = this.controller.removeUserFriend(friendMsg.getUsername(),
				friendMsg.getNameOfFriend());
		if (res == 0)
			return new RemovedFriendMsg(friendMsg.getNameOfFriend());
		if (res == -1)
			return new ErrorMsg("removing was failed");
		// if (res == -2)
		return new ErrorMsg("User who removes must be logged in");
	}

	/**
	 * This method adds a new forum to the list of forums in the system.
	 * 
	 * @param msg
	 *            contains the name of the adder and the subject of new forum.
	 * @return an appropriate message according to the results.
	 */
	public Message getAddForumMsg(AddForumMsg msg) {
		int res = this.controller.addForum(msg.getSubject(), msg.getUsername());
		if (res >= 0) {
			
			return new AddedForumMsg(msg.getUsername(), Integer.toString(res),
					msg.getSubject(), "false");
		}
		if (res == -1)
			return new ErrorMsg("forum with subject "
					+ msg.getSubject() + " already exists");
		if (res == -2)
			return new ErrorMsg("User who adds forum must be logged in");
		if (res == -3)
			return new ErrorMsg("User who adds forum must be a moderator");
		if (res == -4)
			return new ErrorMsg("User who adds forum must be registered");
		// if (res == -5)
		return new ErrorMsg("subject of forum cannot be empty");
	}

	/**
	 * This method creates a new thread in the chosen forum.
	 * 
	 * @param msg
	 *            contains forumID - in to which forum to add a new thread
	 *            topicName of the new thread body of the new thread and the
	 *            author of the new thread.
	 * @return an appropriate message according to the results.
	 */
	public Message getAddThread(CreateThreadMsg msg) {
		int res = this.controller.addThread(msg.getForumID(), msg.getTopicName(),
				msg.getBody(), msg.getUsername());
		if (res >= 0) {
			// return new OkMsg(msg.getUsername(),
			// "thread was added successfully");
			Thread t = this.controller.getThreadByID(res);
			return new CreatedThreadMsg(t, msg.getForumID() + "", false);
		}
		if (res == -1)
			return new ErrorMsg("adding thread failed. Maybe it already exists");
		if (res == -2)
			return new ErrorMsg("User who adds thread must be logged in");
		if (res == -3)
			return new ErrorMsg("User who adds thread must be registered");
		// if (res == -4)
		return new ErrorMsg("Subject cannot be empty");
	}

	/**
	 * Adds a post to the selected thread and the selected forum
	 * 
	 * @param msg
	 *            contains all the necessary information about where we should
	 *            add a new post.
	 * @return an appropriate message according to the results.
	 */
	public Message getAddPostMsg(AddPostMsg msg) {
		int res = this.controller.addPost(msg.getForumID(), msg.getPostID(),
				msg.getTopicName(), msg.getBody(), msg.getUsername());
		
		if (res >= 0) {
			String uName = msg.getUsername();
			int tId = msg.getPostID();

			// Send message to friends
			List<Member> friends = this.controller.getFriendsOfUser(uName);
			for (int i = 0; i < friends.size(); i++) {
				ConnectionHandler ch = this
						.findConnectionHandlerByUsername(friends.get(i).getUsername());
				if (ch != null) {
					FriendNotifyMsg tMsg = new FriendNotifyMsg();
					ch.send(tMsg.toString());
				}
			}

			// Send message to viewers
			List<String> viewers = this.controller.getViewersOfThread(tId);
			for (int i = 0; i < viewers.size(); i++) {
				if(viewers.get(i).equals(uName)) {
					continue;
				}
				ConnectionHandler ch = this
						.findConnectionHandlerByUsername(viewers.get(i));
				if (ch != null && ch.getUsername() != msg.getUsername()) {
					ViewerNotifyMsg tMsg = new ViewerNotifyMsg();
					ch.send(tMsg.toString());
				}
			}

			// Send message to author
			Thread thread = this.controller.getThreadsOfTopic(msg.getUsername(), tId);
			Post toAdd = thread.getRoot();
			String author = toAdd.getAuthor();
			if(!author.equals(uName)) {
				ConnectionHandler ch = this.findConnectionHandlerByUsername(author);
				if (ch != null) {
					AuthorNotifyMsg tMsg = new AuthorNotifyMsg();
					ch.send(tMsg.toString());
				}
			}
			
			Post p = this.controller.getPostByID(res);
			
			return new AddedPostMsg(false, msg.getForumID()
					+ "", p.getID() + "", p.getSubject(), p.getAuthor(),
					p.getDate(), toAdd, p.getContent());
		}
		if (res == -1)
			return new ErrorMsg("adding post failed.");
		if (res == -2)
			return new ErrorMsg("User who adds post must be logged in");
		if (res == -3)
			return new ErrorMsg("User who adds post must be registered");
		// if (res == -4)
		return new ErrorMsg("Subject cannot be empty");
	}

	/**
	 * 
	 * @param msg
	 * @return appropriate msg
	 */
	public Message getEditPostMsg(EditPostMsg msg) {
		int res = this.controller.editPost(msg.getUsername(), msg.getForumID(),
				msg.getPostID(), msg.getContent());
		if (res >= 0) {
			Post p = this.controller.getPostByID(msg.getPostID());
			return new EditedPostMsg(false, msg.getForumID()+"", p.getID()+"", p.getDate(), p.getContent());
			
//			return new EditedPostMsg(msg.getUsername(), p, msg.getForumID()
//					+ "", false);
		}
		if (res == -1)
			return new ErrorMsg("No permission");
		if (res == -2)
			return new ErrorMsg("User who edits post must be logged in");
		if (res == -3)
			return new ErrorMsg("User who edits post must be registered");
		// if (res == -4)
		return new ErrorMsg("Content cannot be empty");
	}

	/**
	 * This method removes a specified post.
	 * 
	 * @param msg
	 *            contains the information about a post to remove.
	 * @return an appropriate message according to the results.
	 */
	public Message getRemovePostMsg(RemovePostMsg msg) {
		int res = this.controller.removePost(msg.getUsername(), msg.getForumID(),
				msg.getPostID());
		if (res == 0)
			return new RemovedPostMsg(Integer.toString(msg.getForumID()), 
					Integer.toString(msg.getPostID()));
		if (res == -1)
			return new ErrorMsg("User must be logged in in order to remove post");
		if (res == -2)
			return new ErrorMsg("User does not have a suitable authority to remove post");
		if (res == -3)
			return new ErrorMsg("Remove post failed. Post does not exist");
		// if (res == -5)
		return new ErrorMsg("Remove post failed. Forum does not exist");
	}

	/**
	 * This method removes the specified thread from the forum.
	 * 
	 * @param msg
	 *            contains all the necessary information about the thread to
	 *            remove.
	 * @return an appropriate message according to the results.
	 */
	public Message getRemoveThreadMsg(RemoveThreadMsg msg) {
		int res = this.controller.removeThread(msg.getUsername(), msg.getForumID(),
				msg.getThreadID());
		if (res == 0)
			return new RemovedThreadMsg(Integer.toString(msg.getForumID()), 
					Integer.toString(msg.getThreadID()));
		if (res == -1)
			return new ErrorMsg("User must be logged in in order to remove thread");
		if (res == -2)
			return new ErrorMsg("User does not have a suitable authority to remove thread");
		if (res == -3)
			return new ErrorMsg("Remove thread failed. Thread does not exist");
		// if (res == -4)
		return new ErrorMsg("Remove thread failed. Forum does not exist");
	}

	/**
	 * This method removes the selected forum from the list of forums.
	 * 
	 * @param msg
	 *            contains the information about a forum to remove.
	 * @return an appropriate message according to the results.
	 */
	public Message getRemoveForumMsg(RemoveForumMsg msg) {
		int res = this.controller.removeForum(msg.getUsername(), msg.getForumID());
		if (res == 0)
			return new RemovedForumMsg(Integer.toString(msg.getForumID()));
		if (res == -1)
			return new ErrorMsg("User must be logged in in order to remove forum");
		if (res == -2)
			return new ErrorMsg("User must be a moderator for removing forum");
		// if (res == -3)
		return new ErrorMsg("Remove forum failed. Forum does not exist");
	}

	/**
	 * 
	 * @param msg
	 * @return OK message which contains the list of forums or error message if
	 *         the user is not logged in.
	 */
	public Message ForumsListReq(GetListOfForumsMsg msg) {
		List<Forum> listOfForums = this.controller.getForums();
		return new GotListOfForumsMsg(listOfForums);
	}

	/**
	 * 
	 * @param msg
	 * @return OK message with the topics of threads in the requested forum and
	 *         error message if the user is not logged in.
	 */
	public Message getForumsTopicsMsg(GetForumTopicsMsg msg) {
		List<Thread> topicsOfForum = this.controller.getThreadsTopicsOfForum(msg.getForumID());
		if (topicsOfForum == null) {
			return new ErrorMsg("User must be logged in in order to see the list of topics of the forum");
		}
		return new GotTopicsOfForumMsg(topicsOfForum, 
				Integer.toString(msg.getForumID()));
	}

	/**
	 * 
	 * @param msg
	 * @return OK message a list of the threads with their posts in the
	 *         requested forum or error message if the user is not logged in.
	 */
	public Message getThreadOfForum(GetThreadMsg msg) {
		Thread thread = this.controller.getThreadsOfTopic(msg.getUsername(), msg.getThreadID());
		if (thread == null) {
			return new ErrorMsg("Error getting the thread");
		}
		int tId = msg.getThreadID();
		this.controller.addViewerByName(msg.getUsername(), tId);
		GotThreadMsg gtm = new GotThreadMsg(Integer.toString(msg.getForumID()), thread);
		return gtm;
	}

	/**
	 * @param msg
	 *            : the message that asks for user's friends
	 * @return GotFriendsListMsg with the list of user's friends if the user is
	 *         registered and logged in or ErrorMsg if the user is not
	 *         registered or logged in
	 */
	public Message getFriendsList(GetFriendsListMsg msg) {

		List<Member> friends = this.controller.getFriendsOfUser(msg.getUsername());

		return new GotFriendsListMsg(friends);
	}

	/**
	 * @return the container of the list of forums and the list of users in the
	 *         system.
	 */
	public Controller getStorage() {
		return this.controller;
	}

	public void addLoggedIn(ConnectionHandler ch) {
		if (!this.loggedIns.contains(ch)) {
			this.loggedIns.add(ch);
		}
	}

	public void removeLoggedIn(ConnectionHandler ch) {
		if (this.loggedIns.contains(ch)) {
			this.loggedIns.remove(ch);
		}
	}

	// /////// Private Funcs /////////
	private ConnectionHandler findConnectionHandlerByUsername(String user) {
		ConnectionHandler ans = null;
		for (int i = 0; i < this.loggedIns.size(); i++) {
			if (this.loggedIns.get(i).getUsername().equals(user)) {
				ans = this.loggedIns.get(i);
			}
		}
		return ans;
	}
}
